package com.zzs.client.dao.redis;

import com.zzs.client.dao.mysql.ProblemMapper;
import com.zzs.common.core.constant.RedisKeyConsts;
import com.zzs.common.core.entity.mysql.ProblemDO;
import com.zzs.common.core.entity.redis.ProblemCache;
import com.zzs.common.core.enums.ResponseCode;
import com.zzs.common.core.util.SnowflakeUtil;
import com.zzs.common.redis.service.RedisService;
import com.zzs.common.security.exception.ServiceException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zzs
 * @date 2025/03/25
 */
@Repository
public class ProblemCacheManager {
    @Autowired
    private RedisService redisService;
    @Autowired
    private ProblemMapper problemMapper;

    public ProblemCache get(long problemId) {
        String key = RedisKeyConsts.getProblemKey(problemId);
        return redisService.getValue(key, ProblemCache.class);
    }

    public void save(ProblemCache problemCache) {
        String key = RedisKeyConsts.getProblemKey(problemCache.getId());
        redisService.saveValue(key, problemCache);
    }

    /**
     * 获取指定难度的分页结果, 如果difficulty是null, 就不指定难度来获取分页结果
     */
    public List<ProblemCache> listProblemCaches(Integer difficulty, int pageSize, int pageNum) {
//        开始索引
        int start = (pageNum - 1) * pageSize;
//        结束索引(包含)
        int end = pageNum * pageSize - 1;
        List<Long> problemIdList = listProblemId(difficulty, start, end);
        if (problemIdList == null || problemIdList.isEmpty()) {
            return null;
        }
        return listProblemCaches(problemIdList);
    }

    /**
     * 得到对应分页的题目id集合
     *
     * @param difficulty 难度, 如果为null, 表示不区分难度
     * @param start      开始索引
     * @param end        结束索引(包含)
     * @return 题目id集合
     */
    private List<Long> listProblemId(Integer difficulty, int start, int end) {
        String key = RedisKeyConsts.getProblemIdZsetKey(difficulty);
        if (redisService.hasKey(key)) {
            return redisService.listValuesFromZset(key, start, end, Long.class);
        } else {
//            对应难度的zset的key丢失了
            List<Long> problemIds = refreshProblemIdZset(difficulty);
            if (problemIds == null || problemIds.isEmpty()) {
                return null;
            }
            return problemIds.subList(start, end + 1);
        }
    }

    /**
     * 通过题目id集合获取对应的题目详情缓存, 如果存在题目缓存为null, 从mysql获取并刷新
     *
     * @param problemIdList 题目id集合
     * @return 题目详情列表
     */
    private List<ProblemCache> listProblemCaches(List<Long> problemIdList) {
//        题目缓存的key集合
        List<String> problemKeyList = new ArrayList<>(problemIdList.size());
        for (Long problemId : problemIdList) {
            problemKeyList.add(RedisKeyConsts.getProblemKey(problemId));
        }
//        获取缓存列表后, 需要判断其中是否有null
        List<ProblemCache> problemCacheList = redisService.listValues(problemKeyList, ProblemCache.class);
//        记录缺失的题目详情缓存对应的题目id
        List<Long> lackProblemIdList = new ArrayList<>();
        for (int i = problemIdList.size() - 1; i >= 0; i--) {
            if (problemCacheList.get(i) == null) {
//                如果这个缓存为空, 记录缺失的id
                lackProblemIdList.add(problemIdList.get(i));
                problemCacheList.remove(i);
            }
        }
        if (lackProblemIdList.isEmpty()) {
//            没有题目缓存丢失
            return problemCacheList;
        }
//        有题目缓存丢失, 获取缺失的题目id详情
        List<ProblemDO> problemDOList = problemMapper.listPublishProblemsByIds(lackProblemIdList);
        Map<String, ProblemCache> lackProblemCacheMap = new HashMap<>();
        for (ProblemDO problemDO : problemDOList) {
            String key = RedisKeyConsts.getProblemKey(problemDO.getId());
            ProblemCache problemCache = new ProblemCache();
            BeanUtils.copyProperties(problemDO, problemCache);
            lackProblemCacheMap.put(key, problemCache);
        }
//        刷新丢失的题目详情缓存
        redisService.saveValue(lackProblemCacheMap);
        problemCacheList.addAll(lackProblemCacheMap.values());
        return problemCacheList;
    }

    public Long getPrevProblemId(long problemId) {
        String key = RedisKeyConsts.getProblemIdZsetKey(null);
        if (!redisService.hasKey(key)) {
            refreshProblemIdZset(null);
        }
        long rank = redisService.getRankFromZSet(key, problemId);
        if (rank == 0) {
            throw new ServiceException(ResponseCode.NOT_FOUND, "没有上一题");
        }
        List<Long> problemIds = redisService.listValuesFromZset(key, rank - 1, rank - 1, Long.class);
        return problemIds.get(0);
    }

    public Long getNextProblemId(long problemId) {
        String key = RedisKeyConsts.getProblemIdZsetKey(null);
        if (!redisService.hasKey(key)) {
            refreshProblemIdZset(null);
        }
        long rank = redisService.getRankFromZSet(key, problemId);
        if (rank == redisService.getSizeForZset(key) - 1) {
            throw new ServiceException(ResponseCode.NOT_FOUND, "没有下一题");
        }
        List<Long> problemIds = redisService.listValuesFromZset(key, rank + 1, rank + 1, Long.class);
        return problemIds.get(0);
    }

    public List<Long> refreshProblemIdZset(Integer difficulty) {
        String key = RedisKeyConsts.getProblemIdZsetKey(difficulty);
//            对应难度的zset的key丢失了
        List<Long> problemIds = problemMapper.listPublishIds(difficulty);
        if (problemIds == null || problemIds.isEmpty()) {
            return null;
        }
//            把对应难度的problemId集合存入redis
        Map<Long, Double> idScoreMap = new HashMap<>();
        for (Long problemId : problemIds) {
            idScoreMap.put(problemId, (double) SnowflakeUtil.getTimestampFromId(problemId));
        }
        redisService.saveForZset(key, idScoreMap);
        return problemIds;
    }
}
